package com.cencat.common.security;

import com.cencat.common.constants.CommonConstants;
import com.cencat.common.context.UserContext;
import com.cencat.common.utils.JwtUtils;
import com.cencat.common.utils.CencatStringUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * JWT认证过滤器
 * 从请求头中提取JWT令牌并进行认证
 * 
 * @author cencat
 * @since 2024-01-01
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    private final JwtTokenProvider jwtTokenProvider;

    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) throws ServletException, IOException {
        
        try {
            // 从请求头中获取JWT令牌
            String token = extractTokenFromRequest(request);
            
            if (CencatStringUtils.isNotBlank(token) && jwtTokenProvider.validateToken(token)) {
                // 解析令牌获取用户信息
                JwtUtils.JwtTokenInfo tokenInfo = jwtTokenProvider.parseToken(token);
                
                if (tokenInfo != null && !tokenInfo.isExpired()) {
                    // 设置用户上下文
                    setUserContext(tokenInfo, token);
                    
                    // 创建认证对象
                    UsernamePasswordAuthenticationToken authentication = createAuthentication(tokenInfo, request);
                    
                    // 设置到Spring Security上下文
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    
                    log.debug("用户认证成功: userId={}, username={}", tokenInfo.getUserId(), tokenInfo.getUsername());
                }
            }
        } catch (Exception e) {
            log.warn("JWT认证过程中发生异常: {}", e.getMessage());
            // 清除认证信息
            SecurityContextHolder.clearContext();
            UserContext.clear();
        }
        
        // 继续过滤链
        filterChain.doFilter(request, response);
    }

    /**
     * 从请求中提取JWT令牌
     * 
     * @param request HTTP请求
     * @return JWT令牌
     */
    private String extractTokenFromRequest(HttpServletRequest request) {
        // 从Authorization头中获取
        String bearerToken = request.getHeader(CommonConstants.AUTHORIZATION_HEADER);
        if (CencatStringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(CommonConstants.TOKEN_PREFIX)) {
            return bearerToken.substring(CommonConstants.TOKEN_PREFIX.length());
        }
        
        // 从请求参数中获取（可选）
        String tokenParam = request.getParameter("token");
        if (CencatStringUtils.isNotBlank(tokenParam)) {
            return tokenParam;
        }
        
        return null;
    }

    /**
     * 设置用户上下文
     * 
     * @param tokenInfo 令牌信息
     * @param token JWT令牌
     */
    private void setUserContext(JwtUtils.JwtTokenInfo tokenInfo, String token) {
        UserContext.setCurrentUserId(tokenInfo.getUserId());
        UserContext.setCurrentUsername(tokenInfo.getUsername());
        UserContext.setCurrentTenantId(tokenInfo.getTenantId());
        UserContext.setJwtToken(token);
        
        // 设置用户角色（如果有）
        if (CencatStringUtils.isNotBlank(tokenInfo.getRoles())) {
            Set<String> roleSet = Arrays.stream(tokenInfo.getRoles().split(","))
                    .map(String::trim)
                    .filter(CencatStringUtils::isNotBlank)
                    .collect(Collectors.toSet());
            UserContext.setCurrentUserRoles(roleSet);
        }
    }

    /**
     * 创建Spring Security认证对象
     * 
     * @param tokenInfo 令牌信息
     * @param request HTTP请求
     * @return 认证对象
     */
    private UsernamePasswordAuthenticationToken createAuthentication(JwtUtils.JwtTokenInfo tokenInfo, 
                                                                    HttpServletRequest request) {
        // 解析用户角色
        List<SimpleGrantedAuthority> authorities = parseAuthorities(tokenInfo.getRoles());
        
        // 创建认证对象
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                tokenInfo.getUsername(), 
                null, 
                authorities
        );
        
        // 设置请求详情
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        
        return authentication;
    }

    /**
     * 解析用户权限
     * 
     * @param roles 角色字符串
     * @return 权限列表
     */
    private List<SimpleGrantedAuthority> parseAuthorities(String roles) {
        if (CencatStringUtils.isBlank(roles)) {
            return List.of();
        }
        
        return Arrays.stream(roles.split(","))
                .map(String::trim)
                .filter(CencatStringUtils::isNotBlank)
                .map(role -> {
                    // 确保角色以ROLE_开头
                    if (!role.startsWith("ROLE_")) {
                        role = "ROLE_" + role;
                    }
                    return new SimpleGrantedAuthority(role);
                })
                .toList();
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        String path = request.getRequestURI();
        
        // 跳过不需要认证的路径
        return path.startsWith("/api/auth/") ||
               path.startsWith("/swagger-ui/") ||
               path.startsWith("/v3/api-docs/") ||
               path.startsWith("/actuator/") ||
               path.equals("/favicon.ico") ||
               path.startsWith("/static/") ||
               path.startsWith("/public/");
    }
}